#encoding=utf-8
'''
功能：
	一个基于epoll的非阻塞网络通信,这要求你的操作系统的内核必须是linux 2.5.44以后的版本
对于I/O处理的几种方式的个人理解:
一个比喻:小明的妈妈每天早上都要等小明起床给他穿衣服，有下面这几种方式：
方法1：在小明的床前守着，等着小明醒来(阻塞式IO)
方法2：先在床头等一段时间，如果这段时间醒来就给他穿衣服，如果没醒就去做其他的(select)
方法3：先去做其他的，如果小明醒来了就叫妈妈，然后来给他穿衣服(poll)

'''


import select
import socket


class EPollLoop(object):
	'''
	一个基于epoll的事件循环的单实例类
	'''

	#epoll的一些事件定义
	#可读事件发生
	_EVENT_RD = select.EPOLLIN
	_EVENT_WR = select.EPOLLOUT
	_EVENT_ERR = select.POLLERR


	_instance = None

	#用于保存tcpserver创建的监听套接字
	_accept_socket = None
	@classmethod
	def instance(cls):
		#返回一个实例化对象
		if cls._instance is None:
			cls._instance = EPollLoop()
			return cls._instance
		else:
			return cls._instance

	@staticmethod
	def set_log_out(logfilename, showlevel = logging.INFO):
	    #设置一下日志信息
	    import logging
	    #设置打印日志
	    logging.basicConfig(filename = logfilename, filemode = 'a', format = "%(filename)s %(lineno)d %(message)s %(asctime)s",\
	                    datefmt = "%d %b %Y %H:%M:%S", level = logging.INFO)
	    #使错误结果在屏幕上打印
	    console = logging.StreamHandler()
	    console.setLevel(logging.INFO)
	    formater = logging.Formatter("%(name)s %(asctime)s %(filename)s %(lineno)d %(message)s")
	    console.setFormatter(formater)
	    logging.getLogger("").addHandler(console)


	def __init__(self):
		#创建epoll对象
		self._epoll = select.epoll()
		#用于保存每个socket的fd和对于的处理句柄和socket对象
		self._handlers = {}
		#创建日志文件 以启动时间来命名
		log_file_name = time.strftime('%Y-%b-%d') + '.log'
		EPollLoop.set_log_out(log_file_name)


	def register_handler(self, sockets, event, handler):
		fd = sockets.fileno()
		self._handlers[fd] = (sockets, handler)
		#将该文件描述符加入epoll池中
		self._epoll.register(fd, event|self._EVENT_ERR)

	def unregister_handler(self, sockets):
		'''
		 unregister the handler and remove handler from this handler list
		'''
		fd = sockets.fileno()
		if fd in self._handlers.keys():
			del self._handlers[fd]
			self._epoll.unregister(fd)
			return True
		else:
			return False

	def update_fd_handler(self, sockets, handler):
		fd = sockets.fileno()
		if fd in self._handlers.keys():
			self._handlers[fd][1] = handler
		else:
			raise _EPollException("con't update the handler this fd isn't be register handler") 

	def update_fd_event(self, sockets, event):
		fd = sockets.fileno()
		if fd in self._handlers.keys():
			self._epoll.modify(fd, event|self._EVENT_ERR)
		else:
			raise _EPollException("con't update the event this fd isn't be register handler")



	def default_err_handler(self, sockets):
		'''
		默认的epoll 错误事件的处理函数
		'''
		self.unregister_handler(sockets)
		#尝试关闭这个socket对象 释放空间
		try:
			sockets.close()
		except:
			pass

	def start(self):
		'''
		一个大大的循环,等待事件发生
		'''
		while True:
			#wait event 
			#print self._handlers.keys()
			event_pairs = self._epoll.poll()
			for fd, events in event_pairs:
				sockets = self._handlers[fd][0]
				handler = self._handlers[fd][1]
				
				if type(handler).__name__ == "genarator":
					#如果handler是一个生成器 则调用send函数继续执行生成器 
					try:
						handler.send(None)
					except :
						logging.warning("gernerator happen a exception...")
						self.default_err_handler(sockets)					
				else:
					#调用注册的回调函数
					try:
						handler(sockets, events)
					except:
						#如果有异常产生，判断是否是监听套接字 如果是则继续循环,否则将此回调函数从回调列表中删除。并从epoll池中注销掉这个socket
						if sockets != EPollLoop._accept_socket:
							logging.info("disconnect client...")
							self.default_err_handler(sockets)
						else:
							logging.warning("a new client accept exception...")
				continue


class _EPollException(BaseException):
	def __init__(self, s):
		self._err = s

	def __str__(self):
		return self._err


